import numpy as np
import pandas as pd
from rdkit import DataStructs
from rdkit.Chem import AllChem
from sklearn.model_selection import GridSearchCV
from sklearn.neural_network import MLPClassifier
import logging


class TuneProject:
    def __init__(self):
        logging.basicConfig(filename='./tune.log', level=logging.DEBUG)

    def read_data(self,train_filename ="reaction_class_model0606.xlsx",test_filename = "reaction_class_model_test622.xlsx" ):
        self.dfc_train = pd.read_excel(train_filename)
        self.dfc_test = pd.read_excel(test_filename)

    def handle(self):
        self.x_train = self.cal_x()
        self.y_train = self.cal_y()
        self.tran_data()

    def cal_x(self):
        ls = []
        for i in range(len(self.dfc_train)):
            S = self.dfc_train['substrate_smiles_canonical'][i]
            try:
                m = AllChem.MolFromSmiles(S)
                fps = AllChem.GetMorganFingerprintAsBitVect(m, 3, nBits=2048)
                array = np.zeros((0,), dtype=np.float64)
                DataStructs.ConvertToNumpyArray(fps, array)
                ls.append(array)
            except (Exception,):
                logging.error(self.dfc_train['substrate_name'][i])
        x_train = np.array(ls)
        return x_train.astype(float)

    def cal_y(self):
        y_train = self.dfc_train.iloc[:, 3:23].to_numpy(dtype=np.float64)
        return y_train.astype(float)

    

    def tran_data(self):
        mlpc_params = {"alpha": [0.0001,0.00001],
               "learning_rate_init": [0.001,0.00001],
               "hidden_layer_sizes": [(100,), (2048,),(2048, 100)],
               "solver": ["adam"],
               "activation": ["relu", "logistic"],
               }
        mlpc = MLPClassifier(random_state=0)  # ANN model object created
        # Model CV process
        mlpc_cv_model = GridSearchCV(mlpc, mlpc_params,
                                    cv=5,  # To make a 5-fold CV
                                    n_jobs=-1,  # Number of jobs to be run in parallel (-1: means to use all processors)
                                    verbose=2)  # Controls the level of detail: higher means more messages gets value as integer.

        mlpc_cv_model.fit(self.x_train, self.y_train)
        logging.info("The best parameters: " + str(mlpc_cv_model.best_params_))

if __name__ == "__main__":
    pj = TuneProject()
    pj.read_data()
    pj.handle()

